package com.jialiang.yojcodesandbox.utils;

import cn.hutool.core.util.StrUtil;
import com.jialiang.yojcodesandbox.model.ExecuteMessage;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;

/**
 * 进程工具类
 */
@Slf4j
public class ProcessUtils {

    /**
     * 执行进程并获取执行信息
     * @param runProcess
     * @param opName
     * @return
     */
    public static ExecuteMessage handleProcessMessage(Process runProcess, String opName) {
        // 进程执行错误码
        int exitCode;
        // 正确输出
        List<String> output = new ArrayList<>();
        // 错误输出
        List<String> errorOutput = new ArrayList<>();
        try {
            // 等待进程执行完毕，获取返回码
            exitCode = runProcess.waitFor();
            // 正常退出
            if (exitCode == 0) {
                // 获取进程的控制台输出：把代码执行后的输出写到输入流，再从输入流中读取
                BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(runProcess.getInputStream(), StandardCharsets.UTF_8));
                // 逐行读取进程的正常输出
                String compileOutputLine;
                while ((compileOutputLine = bufferedReader.readLine()) != null) {
                    output.add(compileOutputLine);
                }
            } else {
                // 异常退出
                BufferedReader errorBufferedReader = new BufferedReader(new InputStreamReader(runProcess.getErrorStream(), getCharsetName()));
                String errorCompileOutputLine;
                while ((errorCompileOutputLine = errorBufferedReader.readLine()) != null) {
                    errorOutput.add(errorCompileOutputLine);
                }
                // 处理错误信息
                errorOutput.set(0, errorOutput.get(0).substring(errorOutput.get(0).lastIndexOf("Main.java")));
                log.error(opName + "失败，原因：" + StringUtils.join(errorOutput, "\n"));
            }
        } catch (Exception e) {
            throw new RuntimeException(opName + "失败，原因：" + e.getMessage());
        }
        return ExecuteMessage.builder()
                .exitCode(exitCode)                                // 设置错误码
                .output(StringUtils.join(output, "\n"))            // 设置正常返回信息
                .errorMessage(StringUtils.join(errorOutput, "\n")) // 设置错误返回信息
                .build();
    }

    /**
     * 获取系统默认字符集
     * 解决获取错误流乱码问题
     */
    public static String getCharsetName() {
        String charaset = StandardCharsets.UTF_8.toString();
        Locale locale = Locale.getDefault();
        if ("zh_CN".equals(locale.toString())) {
            charaset = "GBK";
        }
        return charaset;
    }

    /**
     * 执行交互式进程并获取执行信息，需要用户处理输入输出
     * @param runProcess
     * @param input
     * @param operationName
     * @return
     */
    public static ExecuteMessage handleProcessInteraction(Process runProcess, String input, String operationName) {
        OutputStream outputStream = runProcess.getOutputStream();
        try {
            // 检查进程是否仍在运行
            if (!runProcess.isAlive()) {
                throw new IllegalStateException("进程已结束，无法写入输入");
            }
            outputStream.write((input + "\n").getBytes());
            // 回车操作，执行输入的发生
            outputStream.flush();
            outputStream.close();
            return handleProcessMessage(runProcess, operationName);
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            try {
                outputStream.close();
            } catch (IOException e) {
                log.error("关闭输入流失败：{}", e.getMessage());
            }
        }
    }

    /**
     * 执行交互式进程并获取执行信息
     * @param runProcess
     * @param args
     * @return
     */
    public static ExecuteMessage runInteractProcessAndGetMessage(Process runProcess, String args) {
        // 封装返回对象
        ExecuteMessage executeMessage = new ExecuteMessage();

        try {
            // 向控制台输入程序
            OutputStream outputStream = runProcess.getOutputStream();

            // 向控制台写信息
            OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream);
            // 注意：结尾也要拼接 "\n"
            String[] s = args.split("");
            String join = StrUtil.join("\n", s) + "\n";
            outputStreamWriter.write(join);
            // 回车操作，执行输入的发生
            outputStreamWriter.flush();

            // 分批获取进程的正常输出：把代码执行后的输出写到输入流，再从输入流中读取
            InputStream inputStream = runProcess.getInputStream();
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
            // 拼接输出
            StringBuilder compileOutputStringBuilder = new StringBuilder();

            // 逐行读取输出
            String compileOutputLine;
            while ((compileOutputLine = bufferedReader.readLine()) != null) {
                compileOutputStringBuilder.append(compileOutputLine);
            }
            // 设置正常返回信息
            executeMessage.setOutput(compileOutputStringBuilder.toString());

            // 释放资源，否则会卡死
            outputStream.close();
            outputStream.close();
            inputStream.close();
            runProcess.destroy();
        } catch (Exception e) {
            // 打印异常堆栈
            e.printStackTrace();
        }
        return executeMessage;
    }

}